package com.core.sdk.extra.task;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpUriRequest;

import roboguice.util.RoboAsyncTask;
import android.content.Context;

import com.core.sdk.extra.task.filter.TaskFilter;
import com.core.sdk.extra.task.model.ProgressInfo;
import com.core.sdk.utils.HttpUtil;
import com.core.sdk.utils.HttpUtil.Method;
import com.core.sdk.utils.HttpUtil.ParamSendType;
import com.core.sdk.utils.IOUtil;

public class DownloadTask extends RoboAsyncTask<String> {

	private String url;
	private String savePath;
	private Hashtable<String, String> params;
	private List<TaskFilter<String,ProgressInfo>> taskFilters;

	public DownloadTask(Context context, String url, String savePath) {
		super(context);
		this.url = url;
		this.savePath = savePath;
	}
	public DownloadTask(Context context, String url, String savePath,TaskFilter<String,ProgressInfo> filter) {
		this(context,url,savePath);
		this.addTaskFilter(filter);
	}
	
	private long receivedLength = -1;
	@Override
	public String call() throws Exception {
		FileOutputStream fos = null;
		receivedLength = IOUtil.getFileSize(getTmpDataFileName());
		HttpUriRequest requestUri = HttpUtil.buildRequest(url, Method.GET, params,ParamSendType.text);
		HttpResponse response = HttpUtil.execute(requestUri);
		int responseCode = response.getStatusLine().getStatusCode();
		if (responseCode == HttpStatus.SC_OK) {
			IOUtil.delete(getTmpDataFileName());
			receivedLength = 0;
			fos = new FileOutputStream(getTmpDataFileName());
		} else if (responseCode == HttpStatus.SC_PARTIAL_CONTENT) {
			fos = IOUtil.getFileOutputStream(getTmpDataFileName(), true);
		} else if (responseCode == HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE) {
			receivedLength = 0;
			IOUtil.delete(getTmpDataFileName());
		} else {
			receivedLength = 0;
			IOUtil.delete(getTmpDataFileName());
		}
		boolean downloadFinished = saveDataToFile(response.getEntity(),fos,requestUri);
		if(!downloadFinished){
			throw new RuntimeException("Download the unfinished");
		}
		return savePath;
	}

	public void setParams(Hashtable<String, String> params) {
		this.params = params;
	}
	
	public void addTaskFilter(TaskFilter<String,ProgressInfo> downloadFilter){
		if(taskFilters == null){
			taskFilters = new ArrayList<TaskFilter<String,ProgressInfo>>();
		}
		taskFilters.add(downloadFilter);
	}

	public String getTmpDataFileName() {
		if (savePath != null) {
			int c = savePath.lastIndexOf("/");
			String p = savePath.substring(0, c);
			File f = new File(p);
			if (!f.exists())
				f.mkdirs();
		}
		return savePath + "_bak";
	}

	private boolean saveDataToFile(HttpEntity entity, FileOutputStream fos, HttpUriRequest req) throws Exception {
		if (this.savePath == null)
			return false;
		if (fos == null)
			throw new Exception("saveDataToFile() fos is null");
		if (entity == null) {
			throw new IllegalArgumentException("saveDataToFile() HTTP entity may not be null");
		}
		InputStream is = entity.getContent();
		if (is == null) {
			throw new IllegalArgumentException("saveDataToFile() HTTP InputStream may not be null");
		}
		if (entity.getContentLength() > Integer.MAX_VALUE) {
			throw new IllegalArgumentException("saveDataToFile() HTTP entity too large to be buffered in memory");
		}
		int ttl = (int) entity.getContentLength();
		if (receivedLength > 0) {
			ttl = Long.valueOf(ttl + receivedLength).intValue();
		}
		int n = -1;
		byte[] tmp = new byte[4096];
		try {
			while ((n = is.read(tmp, 0, tmp.length)) != -1) {
				fos.write(tmp, 0, n);
				fos.flush();
				receivedLength += n;
				sendProgressInfo(ttl, receivedLength);
			}
			sendProgressInfo(ttl, receivedLength);
			fos.close();
			fos = null;
			boolean renamed = false;
			if (ttl > 0) {
				if (ttl == receivedLength && receivedLength > 0) {
					renamed = IOUtil.rename(getTmpDataFileName(), savePath);
					if (!renamed)
						IOUtil.delete(savePath);
				}
				return (renamed && ttl == receivedLength && receivedLength > 0);
			}
			if (receivedLength > 0) {
				renamed = IOUtil.rename(getTmpDataFileName(), savePath);
				if (!renamed)
					IOUtil.delete(savePath);
			}
			return renamed;
		} catch (Exception e) {
			throw e;
		} finally {
			closeFileOutPutStream(fos);
			closeInputStream(is, req);
		}
	}

	private void closeFileOutPutStream(FileOutputStream fos) {
		if (fos == null)
			return;
		try {
			fos.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			fos = null;
		}
	}

	private void closeInputStream(InputStream is, HttpUriRequest request) {
		if (is == null)
			return;
		try {
			try {
				request.abort();
			} catch (Exception e) {
				e.printStackTrace();
			}
			is.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			is = null;
		}
	}
	
	private long lastSendTime = System.currentTimeMillis();
	private void sendProgressInfo(long total,long cur){
		if(taskFilters == null)
			return ;
		long tmp = System.currentTimeMillis();
		if(tmp-lastSendTime < 1000 )
			return ;
		lastSendTime = tmp;
		ProgressInfo pi = new ProgressInfo(total, cur);
		for (TaskFilter<String,ProgressInfo> filter : taskFilters) {
			filter.onRunning(pi);
		}
	}
	
	@Override
	protected void onPreExecute() throws Exception {
		super.onPreExecute();
		if(taskFilters == null)
			return ;
		for (TaskFilter<String,ProgressInfo> filter : taskFilters) {
			filter.onPreExecute();
		}
	}
	
	@Override
	protected void onSuccess(String t) throws Exception {
		super.onSuccess(t);
		if(taskFilters == null)
			return ;
		for (TaskFilter<String,ProgressInfo> filter : taskFilters) {
			filter.onSuccess(t);
		}
	}
	
	@Override
	protected void onException(Exception e) throws RuntimeException {
		super.onException(e);
		if(taskFilters == null)
			return ;
		for (TaskFilter<String,ProgressInfo> filter : taskFilters) {
			filter.onException(e);
		}
	}
}
